#include "searcher.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <algorithm>
#include <jsoncpp/json/json.h>
#include "../common/util.hpp"
#include "../common/util.hpp"
namespace searcher{
  const char* const DICT_PATH = "../jieba_dict/jieba.dict.utf8";
  const char* const HMM_PATH = "../jieba_dict/hmm_model.utf8";
  const char* const USER_DICT_PATH = "../jieba_dict/user.dict.utf8";
  const char* const IDF_PATH = "../jieba_dict/idf.utf8";
  const char* const STOP_WORD_PATH = "../jieba_dict/stop_words.utf8";

  Index::Index()
    :jieba_(DICT_PATH, 
            HMM_PATH,
            USER_DICT_PATH,
            IDF_PATH,
            STOP_WORD_PATH)
  {
    std::cout<<"jieba created"<<std::endl;
  }

  const DocInfo* Index::GetDocInfo(uint64_t doc_id)const
  {
    if(doc_id >= forward_index_.size())
    {
      std::cout<<"GetDocInfo Failed!"<<" id is :"\
        <<doc_id<<" size is:"<<forward_index_.size()<<std::endl;
      return nullptr;
    }
    return &forward_index_[doc_id];
  }

  const InvertedList* Index::GetInvertedList(const std::string& key) const
  {
    auto pos = inverted_index_.find(key);
    if(pos == inverted_index_.end())
    {
      std::cout<<"GetInvertedList Failed! "<<" key:"<<key<<std::endl;
      return NULL;
    }
    return &pos->second;
  }
 
  bool Index::Build(const std::string& input_path)
  {
    std::cout<<"Index Build Start"<<std::endl;

    //打开带读文件
    std::ifstream file(input_path.c_str());
    if(!file.is_open())
    {
      std::cout<<"input_path failed! path="<<input_path<<std::endl;
      return false;
    }
    //1.按行读取文件内容
    std::string line;
    while(std::getline(file, line)){ //每次读出的结果自动抛弃'\n'
      //2.构造DocInfo对象 更新正排索引 
      const DocInfo* doc_info = BuildForward(line);
      //3.更新倒排索引
      BuildInverted(*doc_info);
    }
    std::cout<<"Build Finish"<<std::endl;
    file.close();
    return true;
  }
    
  const DocInfo* Index::BuildForward(const std::string& line)
  {
    //切分
    std::vector<std::string> tokens;//存放切分结果
    StringUtill::Split(line, &tokens, "\3");
    if(tokens.size() != 3)
    {
      std::cout<<"tokens not okay!"<<std::endl;
      return nullptr;
    }
    //构造DocInfo对象
    DocInfo doc_info;
    doc_info.title = tokens[0];
    doc_info.url = tokens[1];
    doc_info.content = tokens[2];
    doc_info.doc_id = forward_index_.size();
    //插入正排索引
    forward_index_.push_back(doc_info);
    return &forward_index_.back();
  }
    
  void Index::BuildInverted(const DocInfo& doc_info)
  {
    //对当前的doc_info分词 对正文分词 对标题分词
    std::vector<std::string> title_tokens;
    CutWord(doc_info.title, &title_tokens);
    std::vector<std::string> content_tokens;
    CutWord(doc_info.content, &content_tokens);

    struct WordCnt{
      int title_cnt;
      int content_cnt;
    };

    std::unordered_map<std::string, WordCnt> word_cnt;
    for(std::string word : title_tokens){
      //忽略大小写
      boost::to_lower(word);
      ++word_cnt[word].title_cnt; 
    }
    for(std::string word : content_tokens){
      //忽略大小写
      boost::to_lower(word);
      ++word_cnt[word].content_cnt; 
    }
    for(const auto& word_pair : word_cnt){
      Weight weight;
      weight.doc_id = doc_info.doc_id;
      weight.weight = 10*word_pair.second.title_cnt + word_pair.second.content_cnt;
      weight.key = word_pair.first;
      InvertedList& inverted_list = inverted_index_[word_pair.first]; 
      inverted_list.push_back(weight);
    }
      return;
  }

  void Index::CutWord(const std::string& input, std::vector<std::string>* output)
  {
    jieba_.CutForSearch(input, *output);
  }
  
  /////////////////////////
  bool Searcher::Init(const std::string& input_path)
  {
    return index_->Build(input_path);
  }

  bool Searcher::Search(const std::string& query, std::string* json_result)
  {
    //对查询词进行分词
    std::vector<std::string> tokens;
    index_->CutWord(query, &tokens);
    //触发
    std::vector<Weight> all_token_result;
    for(std::string word : tokens){
      boost::to_lower(word);
      auto* inverted_list = index_->GetInvertedList(word);
      if(inverted_list == nullptr)
      {
        continue;
      }
      all_token_result.insert(all_token_result.end(), inverted_list->begin(), inverted_list->end());
    }
    
    //排序
    //lambda表达式
    std::sort(all_token_result.begin(), all_token_result.end(),
        [](const Weight& w1, const Weight& w2){
          return w1.weight > w2.weight;
        });    
    //构造结果查正排
    
    //同用数据格式：JSON
    //构造JSON结构
    //jsoncpp第三方库
    Json::Value results;
    for(const auto& weight : all_token_result){
      const auto* doc_info = index_->GetDocInfo(weight.doc_id);
      if(doc_info == nullptr)
      {
        continue;
      }
      Json::Value result;  
      result["title"] = doc_info->title;
      result["url"] = doc_info->url;
      result["desc"] = GetDesc(doc_info->content, weight.key);
      results.append(result);
    }
    Json::FastWriter writer;
    *json_result = writer.write(results);
    return true;
  }

  std::string Searcher::GetDesc(const std::string& content, const std::string& key)
  {
    size_t pos = content.find(key);
    if(pos == std::string::npos)
    {
      //该词再正文中不存在，只在标题中出现
      if(content.size() < 160)
      {
        return content;
      }
      else{
        return content.substr(0, 160) + "...";
      }
    }

    size_t beg = pos < 60 ? 0 : pos - 60;
    if(beg + 160 >= content.size())
    {
      return content.substr(beg);
    }
    else{
      return content.substr(beg, 160) + "...";
    }
  }

}//end
